#include "base/base_buffer.h"
#include "base/channel.h"
#include "base/eventloop.h"
#include "base/timeevent.h"
#include "base/timeevery.h"
#include "base/threadpool.h"
#include "base/log.h"
#include "net/connectpool.h"
#include "net/socket.h"
#include "net/heartbeat.h"

#include <memory.h>
#include <tuple>
#include <sys/time.h>

#include "net/connect.h"
namespace net_tools::net{

net_tools::base::Threadpool* Connect::threadpool_ = nullptr;

Connect::Connect(Eventloop* eventloop)
  :User_data_ptr(nullptr),
   buffer_(eventloop,this),
   once_time_job_set_(),
   every_time_job_map_(),
   channel_(nullptr),
   eventloop_(eventloop),
   heartbeat_(nullptr),
   Is_add_heartbeat_(false),
   Is_send_heartbeat_(false),
   read_callback_([](Connect* connect){
     connect->Print_ip_port("recv");}),
   close_callback_([](Connect* connect){
     connect->Print_ip_port("close");}),
   lose_heartbeat_callback_([](Connect* connect){
     connect->Print_ip_port("lose heart beat");})
{
};
Connect::~Connect(){
  NT_LOG_ERROR << "err:Connect been ~" << NT_LOG_ENDL;
};
void Connect::Init_channel(net_tools::base::Channel* channel){
  channel_ = channel;
  buffer_.Init_channel(channel_);
  channel_->Set_write_callback([this]{buffer_.Write_able_callback();});
  channel_->Set_read_callback([this]{buffer_.Read_able_callback();});
  channel_->Set_close_callback([this]{buffer_.Close_callback();});
};
void Connect::Set_address(struct sockaddr_in connect_addr){
  char connect_addr_string[16];
  connect_addr_ = connect_addr;
  Socket::Nettostring(connect_addr_,connect_addr_string);
  connect_addr_string_ = std::string(connect_addr_string);
};
void Connect::Print_ip_port(const char* status){
  NT_LOG_TRACE << "connect from IP: "
               << connect_addr_string_
               << " PORT:"
               << ntohs(connect_addr_.sin_port)
               << " Status: "
               << status
               << NT_LOG_ENDL;
}

unsigned int Connect::Read(void* buf,unsigned int max_size){
  return buffer_.Read(buf,max_size);
};

Buffer_reader* Connect::Read(){
  return buffer_.Read();
};

bool Connect::Write(void* buf,unsigned int len){
  Is_send_heartbeat_ = true;
  return buffer_.Write(buf,len);
};

uint32_t Connect::Peak(){
  return buffer_.Peak();
};
void Connect::Send(){
  buffer_.Send();
};
void Connect::Append(void* buf,uint32_t len){
  buffer_.Append(buf,len);
};

void Connect::Close(){
  buffer_.Close();
};
void Connect::Close_right_now(){
  buffer_.Close_right_now();
};
void Connect::Add_task(func task){
  threadpool_->Addtask(std::move(task));
};
void Connect::Set_read_callback(connect_func cb){
  read_callback_ = std::move(cb);
};
void Connect::Set_close_callback(connect_func cb){
  close_callback_ = std::move(cb);
};
void Connect::Set_lose_heartbeat_callback(connect_func cb){
  lose_heartbeat_callback_ = std::move(cb);
};

void Connect::Read_callback(){
  read_callback_(this);
};
void Connect::Close_callback(){
  close_callback_(this);
  Clean_connect(); //到这整个connect被清理
};
void Connect::Lose_heartbeat_callback(){
  Heartbeat_lose_todo();
  lose_heartbeat_callback_(this);
  buffer_.Clean_buffer();
  Clean_connect();
};
void Connect::User_want_close_callback(){
  NT_LOG_INFO << "clear" << NT_LOG_ENDL;
  Clean_connect(); //到这整个connect被清理
};
void Connect::Heartbeat_Close_connect_todo(){
  heartbeat_->Del_heartbeat(this);
};
void Connect::Heartbeat_lose_todo(){
  heartbeat_->Lose_heartbeat(this);
};
void Connect::Rec_heartbeat(){
  if(heartbeat_) [[likely]] {
    min_heartbeat_struct_->IS_ALIVE |= heartbeat_->index_vector_[min_heartbeat_index_];
  }
};
void Connect::Send_heartbeat(){
  if(!Is_send_heartbeat_){
    NT_LOG_INFO << "send heart beat" << NT_LOG_ENDL;
    Write(nullptr,0);
  }
  Is_send_heartbeat_ = false;
};
void Connect::Clean_connect(){
  NT_LOG_INFO << "Clear" << NT_LOG_ENDL;
  eventloop_->Delevent(channel_);
  eventloop_->Free_channel_right_now(channel_);
  // clean every_time_job
  every_time_job_map_.Del_all_timejob();
  // clean once_time_job;
  once_time_job_set_.Del_all_timejob();
  eventloop_->connectpool_->Free_Connect(this);
};
net_tools::base::Timeevent* Connect::Add_once_time_job_after(func func_,timeval begin_time){
  return eventloop_->Run_after(std::move(func_),begin_time,&once_time_job_set_);
};
net_tools::base::Timeevent* Connect::Add_once_time_job_at(func func_,timeval begin_time){
  return eventloop_->Run_at(std::move(func_),begin_time,&once_time_job_set_);
};
bool Connect::Del_once_time_job(net_tools::base::Timeevent* timeevent){
  return once_time_job_set_.Del_timejob(timeevent);
};
bool Connect::Add_every_time_job(func func_,timeval begin_time,timeval time,int count,const std::string job_name){
  return eventloop_->Run_every(std::move(func_),begin_time,time,count,std::move(job_name),&every_time_job_map_)->Is_takeplace();
};
bool Connect::Del_every_time_job(const std::string job_name){
  return every_time_job_map_.Del_timejob(job_name);
};
void Connect::Add_timejob_after_sec(const std::string job_name,func todo,long int begin_time,long int time,int count){
  timeval now_,sec_;
  gettimeofday(&now_,NULL);
  now_.tv_sec += begin_time;
  sec_.tv_sec = time;
  sec_.tv_usec = 0;
  eventloop_->Run_every(std::move(todo),now_,sec_,count,std::move(job_name),&every_time_job_map_);
};
std::string* Connect::Get_Thread_name(){
  return eventloop_->Get_Thread_name();
};

} // namespace net_tools::net